/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package Compilador;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Locale;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
import java.util.zip.Deflater;
import javax.swing.JFileChooser;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.tools.Diagnostic;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;

/**
 *
 * @author Cristian
 */
public class Compilador {

    private String codigo;
    private File fichero;
    private String direccion;
    private String mensaje;
    private boolean compilado;
    private boolean jar;

    public Compilador() {
        mensaje = "";
        fichero = null;
        direccion = null;
        compilado = false;
        jar = false;
    }

    /**
     * Recibe el Codigo generado y compila la clase
     *
     * @author Cristian
     * @param void
     * @return void
     */
    private void compilar() {


        codigo = Plantilla.getCompleto();
        //System.out.println(codigo);

        if (codigo != null) {

            mensaje += "Compilacion: \n";
            //Creando el fichero
            SimpleJavaFileObject fileObject = new ObjetoCodigoFuenteDinamico("archivos.ar", codigo);
            JavaFileObject javaFileObjects[] = new JavaFileObject[]{fileObject};

            mensaje += "Codigo Fuente: " + fileObject.toString() + "\n";

            /*Instanciamos los Compiladores disponibles*/
            JavaCompiler compiladores = ToolProvider.getSystemJavaCompiler();

            mensaje += "Compiladores: " + compiladores.toString() + "\n";;

            StandardJavaFileManager manejadorArchivos = compiladores.getStandardFileManager(null, Locale.getDefault(), null);

            /* Lista de archivos a Compilar*/
            Iterable<? extends JavaFileObject> archivosParaCompilar = Arrays.asList(javaFileObjects);

            File folder = new File("bin");
            folder.mkdirs();
            direccion = folder.getAbsolutePath();
            //direccion = "/Compilador";
            mensaje += "Ruta de Compilacion: " + direccion + "\n";

            String[] compileOptions = new String[]{"-d", direccion};
            Iterable<String> ConfiSalida = Arrays.asList(compileOptions);

            /*Control y diagnostico de problemas */
            DiagnosticCollector<JavaFileObject> doctor = new DiagnosticCollector<>();

            /*Tareas para el Compilador*/
            JavaCompiler.CompilationTask tareasCompilador = compiladores.getTask(null, manejadorArchivos, doctor, ConfiSalida, null, archivosParaCompilar);

            boolean status = tareasCompilador.call();

            if (!status) {

                for (Diagnostic d : doctor.getDiagnostics()) {
                    System.out.format("Error en la Linea %d in %s", d.getLineNumber(), d);
                    mensaje = "Error en la Linea " + d.getLineNumber() + " in " + d + "\n";
                }
                compilado = false;
            } else {
                System.out.println("Compilacion ok!!!");

                mensaje += "Compilacion ok!!!\n";
                compilado = true;
            }

            try {
                manejadorArchivos.close();//Close the file manager
            } catch (IOException e) {
                e.printStackTrace();
                compilado = false;
            }
        } else {
            mensaje = "No hay Archivos para Compilar";
            compilado = false;
        }


    }

    /**
     * Crea el ejecutable de las clases compiladas en la ruta espesifcada
     *
     * @author Cristian
     * @param void
     * @return void
     */
    private void crearJar() {


        String nombreJar = "Programa.jar";
        nombreJar = fichero.getName();
        //Comprobamos si existe el directorio de salida, si no lo creamos 



        if (nombreJar != null) {
            mensaje += "Creando Ejecutable: \n";

            nombreJar += ".jar";
            //Creamos el fichero jar de salida para los estaticos  
            File jarFile = new File(fichero.getParent(), nombreJar);
            byte[] buffer = new byte[1024];

            mensaje += "Nombre del Ejecutable: " + nombreJar + "\n";

            try {
                //******Crear manifest
                EscritorManifest mw = new EscritorManifest();
                mw.addClassPath("lib/AbsoluteLayout.jar");
                mw.setMainClass(Plantilla.nombreClase);
                //**********************

                //Creamos ejecutable Jar
                JarOutputStream outJar = new JarOutputStream(new FileOutputStream(jarFile), mw.getManifest());
                outJar.setLevel(Deflater.DEFAULT_COMPRESSION);


//Escribiendo el primer fichero ***************************************** 
                JarEntry jarEntry = new JarEntry(Plantilla.nombreClase + ".class");
                outJar.putNextEntry(jarEntry);

                // Leyendo los datos del fichero y escribiendolos en el Jar  
                InputStream input = new FileInputStream(new File(direccion, Plantilla.nombreClase + ".class"));
                int count;
                while ((count = input.read(buffer, 0, 1024)) != -1) {
                    outJar.write(buffer, 0, count);
                }

                outJar.closeEntry();

                //*****Comprobamos si este fichero existe
                File f = new File(direccion + "\\" + Plantilla.nombreClase + "$1.class");
                if (f.exists()) {

                    //Escribiendo el segundo fichero  ***************************
                    jarEntry = new JarEntry(Plantilla.nombreClase + "$1.class");
                    outJar.putNextEntry(jarEntry);

                    // Leyendo los datos del fichero y escribiendolos en el Jar  
                    input = new FileInputStream(new File(direccion, Plantilla.nombreClase + "$1.class"));
                    while ((count = input.read(buffer, 0, 1024)) != -1) {
                        outJar.write(buffer, 0, count);
                    }

                    outJar.closeEntry();
                }

                outJar.close();


            } catch (IOException e) {
                System.out.println("Error escribiendo el jar " + e);
                mensaje = "Error escribiendo el jar " + e;
            }
            System.out.println("Crear .Jar Ok!!");
            mensaje += "Crear .Jar Ok!!\n";
        } else {
            mensaje = "Cancelado!!";

            System.out.println("Cancelado!!");
        }
    }

    /**
     * Obtiene la ruta donde seguardara el ejecutable
     *
     * @author Cristian
     * @param void
     * @return void
     */
    private void ObtenerRuta() {

        JFileChooser jf = new JFileChooser();
        FileNameExtensionFilter filter = new FileNameExtensionFilter("jar", "jar");
        jf.setFileFilter(filter);

        int r = jf.showSaveDialog(jf);
        if (r == JFileChooser.APPROVE_OPTION) {
            fichero = jf.getSelectedFile();
            //String path = file.getAbsolutePath() + ".pl";

            //JOptionPane.showMessageDialog(null, "El archivo se ha exportado exitosamente");

            mensaje += "Nombre: " + fichero.getName() + "\n"
                    + "Ruta: " + fichero.getAbsolutePath() + "\n";

        }
    }

    /**
     * metodo que controla el proceso de creacion del  ejecutable java
     *
     * @author Cristian
     * @param void
     * @return Mensaje del proceso de creacion del ejecutable
     */
        public String ejecutar() {
        mensaje = "\nDetalles de Creacion del  Ejecutable :\n\n";
        ObtenerRuta();
        if (fichero != null) {
            compilar();
            if (compilado) {
                crearJar();
            }
        }

        return mensaje;
    }
}
